package com.espoir.shp.demo;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.vividsolutions.jts.geom.Geometry;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.geotools.data.FeatureSource;
import org.geotools.data.shapefile.ShapefileDataStore;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.data.simple.SimpleFeatureSource;
import org.geotools.feature.FeatureCollection;
import org.geotools.feature.FeatureIterator;
import org.geotools.geojson.feature.FeatureJSON;
import org.opengis.feature.Feature;
import org.opengis.feature.simple.SimpleFeature;
import wcontour.Contour;
import wcontour.Interpolate;
import wcontour.global.Border;
import wcontour.global.PointD;
import wcontour.global.PolyLine;
import wcontour.global.Polygon;

import java.io.*;
import java.net.URLDecoder;
import java.nio.charset.Charset;
import java.util.*;

/**
 * 插值工具
 *
 */
public class interpolationUtil {
    public static void main(String[] args) {
        double[] value = new double[]{33,100};
        String a = "";

        // 获取插值
        double[] result = interpolationUtil.getInterval(value,7);
        String s = Arrays.toString(result);
        System.out.println("s = " + s);
        //test1();
        test2();
        //test3();

    }

    // 原始测试数据
    private static void test1(){
        long start = System.currentTimeMillis();
        interpolationUtil a = new interpolationUtil();
        // data[0] 经度；data[1] 纬度；data[2] value
        double[] bounds = {104.448, 35, 107.5, 39.5};
        double[][] trainData = new double[100][3];
        for (int i = 0; i < 100; i++) {
            double x = bounds[0] + new Random().nextDouble() * (bounds[2] - bounds[0]),
                    y = bounds[1] + new Random().nextDouble() * (bounds[3] - bounds[1]),
                    v = 0 + new Random().nextDouble() * (45 - 0);
/*
            trainData[0][i] = x;
            trainData[1][i] = y;
            trainData[2][i] = v;
*/

            trainData[i][0] = x;
            trainData[i][1] = y;
            trainData[i][2] = v;
        }
        double[] dataInterval = new double[]{20, 25, 30, 35, 40, 45};

        String boundryFile = "E:\\arcgis文件\\各类数据\\水功能区\\ningxia.shp";
        int[] size = new int[]{100, 100};
        boolean isclip = true;
        try {
            String strJson = a.analyze(trainData, dataInterval, size, boundryFile, isclip);
            String strFile = "d:/test1.json";
            a.writeToFile(strFile, strJson);
            System.out.println(strFile + "差值成功, 共耗时" + (System.currentTimeMillis() - start) + "ms");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // 自定义测试数据
    private static void test2(){
        long start = System.currentTimeMillis();
        interpolationUtil util = new interpolationUtil();
        // data[0] 经度；data[1] 纬度；data[2] value
        //double[][] trainData = {{105.507,106.941,105.342},{36.118,38.501,38.115},{500.34,100.45,300.67}};
        double[][] trainData = {{105.507,36.118,500.34},{106.941,38.501,100.45},{105.342,38.115,300.67},{107.342,37.115,200}};
        double[] dataInterval = new double[]{150,200,300,400,450};
        //String boundryFile = "E:\\arcgis文件\\各类数据\\水功能区\\ningxia.shp";
        String boundryFile = "D:\\resource\\gis\\sjdata\\sj.shp";
        //int[] size = new int[]{trainData[0].length, trainData[1].length};
        int[] size = new int[]{251, 313};
        boolean isclip = true;
        try {
            String strJson = util.analyze(trainData, dataInterval, size, boundryFile, isclip);
            String destFilePath = "d:/test2.json";
            util.writeToFile(destFilePath, strJson);
            System.out.println(destFilePath + " 差值成功, 共耗时" + (System.currentTimeMillis() - start) + " ms");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // 武汉测试数据
    private static void test3(){
        long start = System.currentTimeMillis();
        interpolationUtil a = new interpolationUtil();

        File file = new File("D:\\app\\shp\\rain.json");
        String content = null;
        try {
            content = FileUtils.readFileToString(file, "UTF-8");
        } catch (IOException e) {
            e.printStackTrace();
        }
        JSONObject json  = JSON.parseObject(content);

        JSONArray jsonArray = json.getJSONArray("RECORDS");
        Object[] temp = jsonArray.toArray();
        double[][] trainData = new double[500][3];
        for (int i = 0; i < temp.length; i++) {
            JSONObject obj = (JSONObject)temp[i];
            if (StringUtils.isNotEmpty((String) obj.get("ESLO")) && StringUtils.isNotEmpty((String) obj.get("NTLA"))) {
                trainData[i][0] = Double.parseDouble((String)obj.get("ESLO"));
                trainData[i][1] = Double.parseDouble((String)obj.get("NTLA"));
                trainData[i][2] = Double.parseDouble((String)obj.get("SR"));
            }
        }
        double[] dataInterval = new double[]{10, 25, 50, 100};
        String boundryFile = "D:\\app\\shp\\wh_area.shp";
        int[] size = new int[]{200, 200};
        boolean isclip = true;
        try {
            String strJson = a.analyze(trainData, dataInterval, size, boundryFile, isclip);
            String strFile = "d:/test3.json";
            a.writeToFile(strFile, strJson);
            System.out.println(strFile + "差值成功, 共耗时" + (System.currentTimeMillis() - start) + "ms");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    public String execute(double[][] trainData, double[] dataInterval, int[] size, boolean clipped)throws Exception{
        String resourcePath = interpolationUtil.class.getResource("/").getPath();
        resourcePath = URLDecoder.decode(resourcePath.substring(1,resourcePath.length()), "utf-8");
        String maskFilePath = resourcePath+"static/data/GeoData/mask.shp";
        interpolationUtil IU = new interpolationUtil();
        return IU.analyze(trainData,dataInterval,size,maskFilePath,clipped);
    }

    /**
     * 等值面制作方法
     * @param trainData    散点数据
     * @param dataInterval 数据间隔（高差数组）
     * @param size         大小，宽，高
     * @param maskFilePath 矢量裁剪数据路径
     * @param clipped      是否裁剪
     * @return             GeoJson面数据
     */

    public  String analyze(double[][] trainData, double[] dataInterval, int[] size, String maskFilePath, boolean clipped) {
        String PolygonResult = "";
        try {
            double _undefData = -9999.0;
            int width = size[0];
            int height = size[1];
            double[] _X = new double[width];
            double[] _Y = new double[height];
            File file = new File(maskFilePath);
            ShapefileDataStore shpDataStore = new ShapefileDataStore(file.toURI().toURL());

            //设置编码
            Charset charset = Charset.forName("GBK");
            shpDataStore.setCharset(charset);
            // 获取文件名
            String typeName = shpDataStore.getTypeNames()[0];
            SimpleFeatureSource featureSource = shpDataStore.getFeatureSource(typeName);
            SimpleFeatureCollection fc = featureSource.getFeatures();

            double minX = fc.getBounds().getMinX();
            double minY = fc.getBounds().getMinY();
            double maxX = fc.getBounds().getMaxX();
            double maxY = fc.getBounds().getMaxY();
            Interpolate.createGridXY_Num(minX, minY, maxX, maxY, _X, _Y);
            int nc = dataInterval.length;
            // IDW插值
            double[][] _gridData = Interpolate.interpolation_IDW_Neighbor(trainData, _X, _Y, 12, _undefData);
            int[][] S1 = new int[_gridData.length][_gridData[0].length];
            List<Border> _borders = Contour.tracingBorders(_gridData, _X, _Y, S1, _undefData);
            // 生成等值线
            List<PolyLine> cPolylineList = Contour.tracingContourLines(_gridData, _X, _Y, nc, dataInterval, _undefData, _borders, S1);
            // 平滑
            cPolylineList = Contour.smoothLines(cPolylineList);
            List<Polygon> cPolygonList = Contour.tracingPolygons(_gridData, cPolylineList, _borders, dataInterval);
            PolygonResult = getPolygonGeoJson(cPolygonList);
            if (clipped) {
                SimpleFeatureCollection polygonCollection = GeoJSONUtil.readGeoJsonByString(PolygonResult);
                FeatureSource dc = clipFeatureCollection(fc, polygonCollection);
                PolygonResult = getPolygonGeoJson(dc.getFeatures());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return PolygonResult;
    }

    /**
     * 裁剪方法
     * @param fc 裁剪范围
     * @param gs 被裁剪要素
     * @return 裁剪之后结果
     */
    public FeatureSource clipFeatureCollection(FeatureCollection fc, SimpleFeatureCollection gs) {
        try {
            List<Map<String, Object>> values = new ArrayList<Map<String, Object>>();
            FeatureIterator contourFeatureIterator = gs.features();
            FeatureIterator dataFeatureIterator = fc.features();
            while (dataFeatureIterator.hasNext()) {
                Feature dataFeature = dataFeatureIterator.next();
                Geometry dataGeometry = (Geometry) dataFeature.getProperty("the_geom").getValue();
                while (contourFeatureIterator.hasNext()) {
                    Feature contourFeature = contourFeatureIterator.next();
                    Geometry contourGeometry = (Geometry) contourFeature.getProperty("geometry").getValue();
                    double lv = (Double) contourFeature.getProperty("lvalue").getValue();
                    double hv = (Double) contourFeature.getProperty("hvalue").getValue();
                    if (dataGeometry.intersects(contourGeometry)) {
                        Geometry geo = dataGeometry.intersection(contourGeometry);
                        Map<String, Object> map = new HashMap<>();
                        map.put("the_geom", geo);
                        map.put("lvalue", lv);
                        map.put("hvalue", hv);
                        values.add(map);
                    }
                }
            }
            contourFeatureIterator.close();
            dataFeatureIterator.close();
            SimpleFeatureCollection sc = FeaureUtil.creatSimpleFeatureByFeilds("polygons", "crs:4326,the_geom:MultiPolygon,lvalue:double,hvalue:double", values);
            return FeaureUtil.creatFeatureSourceByCollection(sc);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * FeatureCollection转GeoJson
     * @param fc FeatureCollection要素
     * @return GeoJson数据
     */
    public String getPolygonGeoJson(FeatureCollection fc) {
        FeatureJSON fjson = new FeatureJSON();
        StringBuffer sb = new StringBuffer();
        try {
            sb.append("{\"type\": \"FeatureCollection\",\"features\": ");
            FeatureIterator itertor = fc.features();
            List<String> list = new ArrayList<>();
            while (itertor.hasNext()) {
                SimpleFeature feature = (SimpleFeature) itertor.next();
                StringWriter writer = new StringWriter();
                fjson.writeFeature(feature, writer);
                list.add(writer.toString());
            }
            itertor.close();
            sb.append(list.toString());
            sb.append("}");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return sb.toString();
    }

    /**
     * List<Polygon>转GeoJson
     * @param cPolygonList List<Polygon>要素集合
     * @return GeoJson数据
     */
    private String getPolygonGeoJson(List<Polygon> cPolygonList) {
        String geo = null;
        String geometry = " { \"type\":\"Feature\",\"geometry\":";
        String properties = ",\"properties\":{ \"hvalue\":";

        String head = "{\"type\": \"FeatureCollection\"," + "\"features\": [";
        String end = "  ] }";
        if (cPolygonList == null || cPolygonList.size() == 0) {
            return null;
        }
        try {
            for (Polygon pPolygon : cPolygonList) {
                List<Object> ptsTotal = new ArrayList<>();
                List<Object> pts = new ArrayList<>();
                PolyLine pline = pPolygon.OutLine;
                for (PointD ptD : pline.PointList) {
                    List<Double> pt = new ArrayList<>();
                    pt.add(ptD.X);
                    pt.add(ptD.Y);
                    pts.add(pt);
                }
                ptsTotal.add(pts);
                if (pPolygon.HasHoles()) {
                    for (PolyLine cptLine : pPolygon.HoleLines) {
                        List<Object> cpts = new ArrayList<>();
                        for (PointD ccptD : cptLine.PointList) {
                            List<Double> pt = new ArrayList<>();
                            pt.add(ccptD.X);
                            pt.add(ccptD.Y);
                            cpts.add(pt);
                        }
                        if (cpts.size() > 0) {
                            ptsTotal.add(cpts);
                        }
                    }
                }
                JSONObject json = new JSONObject();
                json.put("type", "Polygon");
                json.put("coordinates", ptsTotal);
                double hv = pPolygon.HighValue;
                double lv = pPolygon.LowValue;
                if (hv == lv) {
                    if (pPolygon.IsClockWise) {
                        if (!pPolygon.IsHighCenter) {
                            hv = hv - 0.1;
                            lv = lv - 0.1;
                        }

                    } else {
                        if (!pPolygon.IsHighCenter) {
                            hv = hv - 0.1;
                            lv = lv - 0.1;
                        }
                    }
                } else {
                    if (!pPolygon.IsClockWise) {
                        lv = lv + 0.1;
                    } else {
                        if (pPolygon.IsHighCenter) {
                            hv = hv - 0.1;
                        }
                    }

                }
                geo = geometry + json.toString() + properties + hv + ", \"lvalue\":" + lv + "} }" + "," + geo;
            }
            if (geo.contains(",")) {
                geo = geo.substring(0, geo.lastIndexOf(","));
            }
            geo = head + geo + end;
        } catch (Exception e) {
            e.printStackTrace();
            return geo;
        }
        return geo;
    }

    /**
     * 字符串写成文件
     * @param filePath json文件路径
     * @param content 写入文件内容
     */
    public void writeToFile(String filePath, String content) {
        FileWriter fw = null;
        try {
            //如果文件存在，则追加内容；如果文件不存在，则创建文件
            File f = new File(filePath);
            if(f.exists()){
                f.delete();
            }
            fw = new FileWriter(f, true);
        }
        catch (IOException e) {
            e.printStackTrace();
        }
        PrintWriter pw = new PrintWriter(fw);
        pw.println(content);
        pw.flush();
        try {
            fw.flush();
            pw.close();
            fw.close();
        }
        catch (IOException e) {
            e.printStackTrace();
        }
    }


    /**
     * 获取等值线分隔区间
     * @param type 产品类型
     * @return 分隔数据
     */
    public double[] getDataInterval(String type){
        double[] dataInterval;
        if(type.equals("depth")){
            // 深度
            dataInterval = new double[]{50, 500, 1000, 1500, 2000, 3000,3500,4000,4500,5000,5500,6000,6500};
        }else if(type.equals("MaxEle")){
            // 最大潮位值
            dataInterval = new double[]{0.3, 0.5, 1, 1.5, 2, 3,4.5};
        }else if(type.equals("MaxFD")){
            // 最大淹没水深
            dataInterval = new double[]{0.15, 0.5, 0.8, 1.2, 2, 5};
        }else if(type.equals("Tide")){
            // 风暴潮
            dataInterval = new double[]{0.3, 0.5, 1, 1.5, 2, 3,4.5};
        }else if(type.equals("FortLand")){
            // 风暴潮水深变化
            dataInterval = new double[]{0.3, 0.5, 1, 1.5, 2, 3,4.5};
        }else{
            dataInterval = new double[]{0.3, 0.5, 1, 1.5, 2, 3,4.5};
        }
        return dataInterval;
    }
    /**
     * 根据输入数据确定划分区间
     * @param value 数据数组
     * @param classes 界别数量
     * @return 分割区间
     */
    public static  double[] getInterval(double[] value,int classes){
        Arrays.sort(value);
        double min = value[0];
        double max = value[value.length-1];
        double dx = (max-min)/classes;
        double[] dataInterval = new double[classes-1];
        for(int i=0;i<classes-1;i++){
            dataInterval[i] = (double)Math.round(((i+1)*dx+min)*100)/100;
        }
        return dataInterval;
    }
}